package com.sz.youban.dt.service.allinpay;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.sz.youban.dt.entity.allinpay.DtPayInvokeRecords;
import com.sz.youban.dt.entity.allinpay.DtPayNotifyRecords;
import com.sz.youban.dt.entity.allinpay.DtPayRecords;
import com.sz.youban.dt.entity.pay.PayNotifyStatus;
import com.sz.youban.dt.interfs.allinpay.IDtPayInvokeRecordsService;
import com.sz.youban.dt.model.AppInvokeRequest;
import com.sz.youban.dt.model.AppInvokeResponse;
import com.sz.youban.dt.model.PairDef;
import com.sz.youban.dt.service.allinpay.mapper.DtPayInvokeRecordsMapper;
import com.sz.youban.dt.service.allinpay.mapper.DtPayNotifyRecordsMapper;
import com.sz.youban.dt.service.allinpay.mapper.DtPayRecordsMapper;
import com.sz.youban.dt.service.utils.GO;
import com.sz.youban.dt.service.utils.GV;
import com.sz.youban.dt.service.utils.HttpUtils;

/**
 * <p>
 * 支付回调地址记录 服务实现类
 * </p>
 *
 * @author ruichar
 * @since 2017-06-15
 */
@Service
public class DtPayInvokeRecordsServiceImpl extends ServiceImpl<DtPayInvokeRecordsMapper, DtPayInvokeRecords> implements IDtPayInvokeRecordsService {

    private final Logger logger = LoggerFactory.getLogger(DtPayInvokeRecordsServiceImpl.class);

    @Autowired
    private DtPayNotifyRecordsMapper payNotifyRecordsMapper;

    @Autowired
    private DtPayInvokeRecordsMapper payInvokeRecordsMapper;

    @Autowired
    private DtPayRecordsMapper payRecordsMapper;

    @Value("${invokeDomain}")
    private String invokeDomain;
    private final ExecutorService pool = Executors.newFixedThreadPool(20);

    /**
     * 扫描需要通知app交易结果的记录
     */
    @Override
    public void scanPayNotify() {
        logger.info("---------- 进入scanPayNotify方法，准备开始定时回调任务 ----------");

        //==========1，获取到要进行通知的所有记录（未通知，且提交时间2小时之内）==========
        List<DtPayNotifyRecords> list = payNotifyRecordsMapper.getPayNotifyRecords(PayNotifyStatus.NOTIFY_NO.getStatus());
        if (list.size() == 0) {
            return;
        }

        List<Future<AppInvokeResponse>> futureResultlist = new ArrayList<Future<AppInvokeResponse>>();

        //==========2，遍历 list ==========
        for (DtPayNotifyRecords payNotifyRecords : list) {

            //==========2.1，查询要回调的url==========
            DtPayInvokeRecords payInvokeRecords = payInvokeRecordsMapper.getPayInvokeByDealNo(payNotifyRecords.getDealNo());
            String urlSuffix = payInvokeRecords.getUrl();
            if (StringUtils.isBlank(urlSuffix)) {
                logger.error("没有对应的回调地址，数据异常");
                continue;
            }
            String url = urlSuffix.startsWith("/") ? invokeDomain + urlSuffix : invokeDomain + "/" + urlSuffix;
            //==========2.2，查询要回调的结果==========
            DtPayRecords payRecordsParam = new DtPayRecords();
            payRecordsParam.setDealNo(payNotifyRecords.getDealNo());
            List<DtPayRecords> listRecords = payRecordsMapper.selectPayRecordsByParam(payRecordsParam);
            if (listRecords.size() == 0) {
                return;
            }
            DtPayRecords payRecords = listRecords.get(0);       //此payRecords为要回调的结果

            //==========2.3，向app方发起通知并接受结果==========
            AppInvokeRequest appInvokeRequest = new AppInvokeRequest();
            appInvokeRequest.setSatatus(payRecords.getStatus());
            appInvokeRequest.setDealNo(payRecords.getDealNo());
            appInvokeRequest.setOrderNo(payRecords.getOrderNo());
            appInvokeRequest.setSettleDay(payRecords.getSettleDay());
            appInvokeRequest.setMsg(payRecords.getDesc());

            PairDef<String, AppInvokeRequest> pd = new PairDef<String, AppInvokeRequest>();
            pd.setFirst(url);
            pd.setSecond(appInvokeRequest);

            Callable<AppInvokeResponse> callable = new MyCallable(pd);
            Future<AppInvokeResponse> future = pool.submit(callable);
            futureResultlist.add(future);

            this.setThreadTimeOut(futureResultlist);
        }

    }

    /**
     * 设置线程超时时间
     */
    private void setThreadTimeOut(List<Future<AppInvokeResponse>> futureResultlist) {
        for (Future<AppInvokeResponse> item : futureResultlist) {
            
            //设置每个线程的超时时间
            try 
            {
                item.get(10, TimeUnit.SECONDS);//10s
            } catch (InterruptedException e) {
                logger.error("=======InterruptedException:{}", e);
            } catch (ExecutionException e) {
                logger.error("=======ExecutionException:{}", e);
            } catch (TimeoutException e) {
                logger.error("=======TimeoutException:{}", e);
            }catch(Exception e)
            {
            	logger.error("====ex:{}",e);
            }

        }
    }

    class MyCallable implements Callable<AppInvokeResponse> {
        private PairDef<String, AppInvokeRequest> pd;

        MyCallable(PairDef<String, AppInvokeRequest> pd) {
            this.pd = pd;
        }

        @Override
        public AppInvokeResponse call() {
//            return appInvokeRequest+"任务返回的内容";
            return postApp(pd);
        }
    }

    private AppInvokeResponse postApp(PairDef<String, AppInvokeRequest> pd) {

        AppInvokeRequest appInvokeRequest = pd.getSecond();
        String url = pd.getFirst();

        //----------1，组装参数，请求app接口----------
        Map<String, String> map = new HashMap<>();
        //将status 的值进行转换，与同步回调时的code 保持一直。参考接口文档：http://172.18.0.25:8090/workspace/myWorkspace.do?projectId=3#73
        int status = appInvokeRequest.getSatatus();
        int code = 0;
        if(status == 1){
            code = 10000;
        }else if(status == 0){
            code = 102;
        }
        map.put("code", GO.string(code));
        map.put("dealNo", appInvokeRequest.getDealNo());
        map.put("orderNo", appInvokeRequest.getOrderNo());
        String settle = "";
        if(appInvokeRequest.getSettleDay() != null){
            settle = appInvokeRequest.getSettleDay();
        }
        map.put("settleDay", settle);
        map.put("msg", appInvokeRequest.getMsg());
        logger.info("---------- 即将传输给app的数据为  ----------{}-----url:{}" , map,url);

        try 
        {
            //----------2，发起http请求----------
            String result = HttpUtils.doPostJson(url, map);
            logger.info("----------向app端回调的返回结果为resutl：{}",result);
          //----------3，根据app返回的结果进行判断是否通知成功，根据deal_no 修改 dt_pay_notify_records 中的 status----------
            JSONObject jsonResult = JSONObject.parseObject(result);
            if(GV.i(jsonResult.get("ret")) == 200){
                JSONObject content = (JSONObject) jsonResult.get("content");
                String retCode = GO.string(content.get("code"));
                if (GV.i(retCode) == 10000) {
                    //app收到通知，修改数据的“通知状态”
                    payNotifyRecordsMapper.updateStatusByDealNo(appInvokeRequest.getDealNo(), PayNotifyStatus.NOTIFY_YES.getStatus());
                    logger.info("----- app端收到回调=数据，修改db状态成功 dealNo={}  ----------",appInvokeRequest.getDealNo());
                }
                else
                {
                	logger.warn("----- app端收到异常回掉数据 dealNo={}，code={}  ----------",appInvokeRequest.getDealNo(),retCode);
                }
            }
        }
        catch (Exception e) 
        {
        	logger.error("ex:{}",e);
        }
        //----------4，整理app的返回参数（空）暂时没有用处----------
        AppInvokeResponse appInvokeResponse = new AppInvokeResponse();
        return appInvokeResponse;
    }

}
